//  四、手写实现MyPromise源码
// 要求：尽可能还原Promise中的每一个API,并通过注释的方式描述思路和原理
const PEDDING = 'pedding'
const FULLFILLED = 'fullfiled'
const REJECTED = 'rejected'
class MyPromise {
    status = PEDDING //当前promise状态  修改后不可更改
    value = undefined //成功传入的值
    error = undefined //失败传入的值
    successCb = [] //成功回调
    failCb = [] //失败回调
    constructor(execuctor) {
        // 捕获execuctor执行出错处理，代码出错，直接调用reject
        try {
            execuctor(this.resolve, this.reject)
        } catch (e) {
            this.reject(e)
        }
    }
    // 成功
    resolve = (value) => {
        // status 修改后不可更改
        if (this.status !== PEDDING) {
            return
        }
        this.status = FULLFILLED
        // 保存成功传入的值
        this.value = value
        // 挨个执行所有的成功回调
        while (this.successCb.length) {
            this.successCb.shift()()
        }
    }
    // 失败
    reject = (error) => {
        // status 修改后不可更改
        if (this.status !== PEDDING) {
            return
        }
        this.status = REJECTED
        // 保存失败传入的信息
        this.error = error
        // 挨个执行所有的失败回调
        while (this.failCb.length) {
            this.failCb.shift()()
        }
    }
    // 使用this.then是为了返回一个promise对象   
    // 不管上次是成功还是失败都执行finally传入的回调函数
    // 成功就将调用下一个成功回调 失败就调用下一个失败回调
    finally(callback) {
        return this.then(res => {
            // Mypromise.resolve的目的是为了等待回调函数执行完成
            return MyPromise.resolve(callback()).then(() => res)
        }, err => {
            callback()
            // throw抛出的一次异常 可以被promise执行回调过程中的try catch 捕获到，并通过reject传递给下一个失败回调
            return MyPromise.resolve(callback()).then(() => {
                throw err
            })
        })
    }
    
     catch (failCb) {
        return this.then(undefined, failCb)
    }
    static all(arr) {


        return new MyPromise((resolve, reject) => {

            let res = []
            let count = 0
            // 按传入顺序将结果存起来
            function addData(index, item) {
                res[index] = item
                count++
                // 如果这两个值相等，说明所有异步代码都已执行完毕
                if (count === arr.length) {
                    resolve(res)
                }
            }
            // 遍历传入的数组，将结果按顺序存起来
            arr.forEach((item, index) => {
                if (item instanceof MyPromise) {
                    // promise对象 等待promise执行完毕后添加到数组res中
                    // 如果执行出错，直接调用reject
                    item.then(res => addData(index, res), reject)
                } else {
                    // 普通值
                    addData(index, item)
                }
            })
        })
    }
    //返回promise对象
    static resolve(value) {
        // 是promise对象直接返回
        if (value instanceof MyPromise) {
            return value
        }
        // 将传入的值转化成promise对象
        return new MyPromise(resolve => resolve(value))
    }
    then = (successCb, failCb) => {
        // 链式调用，需要返回一个新的MyPromise
        /**
         * 实例化MyPromise会立即执行传入的execuctor，可以将下面代码放入到实例化MyPromise的参数execuctor中
          if (this.status === FULLFILLED) {
                successCb(this.value)
            } else if (this.status === REJECTED) { //失败
                failCb(this.error)
            } else {
                // 等待 将成功回调和失败回调存起来  异步任务
                this.successCb.push(() => {
                    successCb(this.value)
                })
                this.failCb.push(() => {
                    failCb(this.error)
                })
            }
         */
        // 如果.then没有回调函数，就将值传递给下一个回调函数
        successCb = isFunc(successCb) ? successCb : value => value
        failCb = isFunc(failCb) ? failCb : error => error

        // console.log(! successCb )
        const newPromise = new MyPromise((resolve, reject) => {
            // setTimeout为了确保new MyPromise执行完

            setTimeout(() => {
                // 成功 同步
                if (this.status === FULLFILLED) {
                    // 成功回调的返回值  代码执行出错，直接调用下一个函数reject传递给下一个then的回调函数
                    try {
                        let res = successCb(this.value)
                        resolvePromise(newPromise, res, resolve, reject)


                    } catch (e) {
                        reject(e)
                    }
                    // 实现成功回调的返回值传入到下一个then回调函数中 
                    // resolvePromise(res, resolve, reject)
                } else if (this.status === REJECTED) { //失败
                    try {
                        let res = failCb(this.error)
                        // 实现失败回调的返回值传入到下一个then回调函数中 
                        resolvePromise(newPromise, res, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                } else {
                    // 等待 将成功回调和失败回调存起来  异步任务
                    this.successCb.push(() => {
                        try {
                            let res = successCb(this.value)
                            resolvePromise(newPromise, res, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }

                    })
                    this.failCb.push(() => {
                        try {
                            let res = failCb(this.error)
                            resolvePromise(newPromise, res, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    })
                }
            }, 0)

        })
        return newPromise
    }
}

function isFunc(fn) {
    return typeof fn === 'function'
}
/*
两种情况
1、先执行resolve或reject,修改状态为FULLFILLE或REJECTED,保存成功传入的值和失败传入的值
   执行.then的时候就会执行successCb(this.value)（将返回值传递给.then的回调函数）
2、先执行.then,将成功和失败回调存起来，在执行resolve或reject修改相应的状态 执行相应的回调
*/
/**
 * 识别返回的promise对象
 * 如果成功回调返回的是一个普通值，
 * 可以通过resolve(res)将值存起来或传递给.then
 * 当调用.then时候，就会执行successCb(this.value)（将返回值传递给.then的回调函数）
 * 状态为PEDDING
 * .then的时候会将成功回调存起来，resolve或reject的时候会相应回调函数并修改相应的状态
 * 
 * 返回的是一个MyPromise对象
 * 返回的promise.then(res=>res)方法拿到，然后再执行resolve(res)将值传递下去（具体流程就是上面说的两种情况）
 *  */
function resolvePromise(newPromise, res, resolve, reject) {
    // 判断成功回调返回的值是不是跟当前MyPromise对象自己
    if (newPromise === res) {
        return reject(new Error('Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>'))
    }

    // 识别返回的promise对象
    if (res instanceof MyPromise) {
        res.then(resolve, reject) //等同于  res.then(res=>resolve(res),err=>reject(err))
    } else {
        // 普通值
        resolve(res)
    }
}
var p1 = new Promise((resolve, reject) => {
    // resolve(123)
    // setTimeout(()1 => {
    reject('timeout')
    // }, 1000)
})
// p1.then(res => {
//     console.log(res)
// }, err => {
//     console.log(err)
// })
// let p2 = p1.then(res => {
//     return p2
// }, err => {
//     console.log('失败' + err)
//     return err
// })
// p1.then(123).then(res => {
//     console.log(res)
// })
// var p0 = new Promise((resolve) => {
//     setTimeout(() => {
//         resolve(123)

//     }, 2000)
// })
// Promise.all([1, 2, 3, p0, 3]).then(res => {
//     console.log(res)
// })
// p1.then(res=>{
//     console.log(res)
//     return '哈哈哈哈'
// }).finally(()=>{
//     console.log('hahah')
//     return '123'
// }).then(res=>{
// console.log(res)
// })
// p1.then(res=>{
//     return new MyPromise((resolve)=>{
//         setTimeout(()=>{
//             resolve('dafasf')

//         },3000)
//     })
// }).then(res=>{
//     console.log(res)
// })
p1.catch(e => {
    console.log('失败了')
    console.log(e)
}).then(res => {
    console.log(res)
})